#include "common.h"

int substrfile(const char * filename)
{
	char buf[10] = {};
	const char *p = filename;
	int pos = 0;
	for( ; *p != '\0' ; ++p)
	{
		if(*p == '.')
		{			
			return pos;
		}
		++pos;
	}
}


int Findfile(const char * path)
{
	DIR * dir = opendir(path);
		if(dir == NULL) perror("opendir") , exit(-1);
	struct dirent * dirent = readdir(dir);	
	int temp = 0;
	char filename[10] = {};
	while((dirent = readdir(dir)) != NULL)
	{		
		if(dirent->d_type == 8)
		{
			strncpy(filename,dirent->d_name,substrfile(dirent->d_name));
			if(atoi(filename) > temp)
			{
				temp = atoi(filename);
			}
		}		
	}
	
	if(access("./user/0.dat" , F_OK) == -1)
	{
		return 0;
	}
	return temp + 1;
}


int establishAccount(struct Account acc)
{
	char buf[100] = {};	
	sprintf(buf, "./user/%d.dat", acc.num);
	int fd = open(buf, O_CREAT | O_RDWR , 0666);
	if(fd == -1)
		perror("open failed!") , exit(-1);
	if(write(fd , &acc , sizeof(acc)) == -1)
	{
		perror("creating a new account failed!") ;
		return -1;	
	}
	printf("create a new account , id : %d , name : %s\n",acc.num,acc.name);
	close(fd);
	return 1;		
}


int closeAccount(struct Account acc)
{
	char buf[100] = {};	
	sprintf(buf, "./user/%d.dat", acc.num);
	if(access( buf , F_OK) == -1)
	{
		return 0;
	}
	else
	{
		remove(buf);
		printf("close the account of id : %d successfully .\n",acc.num);
		return 1;
	}	
}


int saveAccount(struct Account acc)
{
	char buf[100] = {};	
	sprintf(buf, "./user/%d.dat", acc.num);
	struct Account currentAcc;	
	//1. check if file exists.
	if(access( buf , F_OK) == -1)
	{
		printf("The account of id :%d doesn't exists !\n",acc.num);
		return 0;
	}
	else
	{
		int fd = open(buf , O_RDWR , 0600);
		if(fd == -1)
			perror("fail to open.") , exit(-1);
	//update data of the account.
	//lock the file before being writen.
		struct flock lock;
		lock.l_type = F_WRLCK;
		lock.l_whence = SEEK_SET;
		lock.l_start = 0;
		lock.l_pid = -1;
		int res = fcntl(fd , F_SETLK , &lock);

		read(fd , &currentAcc , sizeof(currentAcc));
		currentAcc.balance += acc.balance;

		//printf("acc.balance :%f\n",acc.balance); 
		//printf("currentAcc.balance :%f\n",currentAcc.balance);

	//rewrite , very important ..
		lseek(fd , 0 , SEEK_SET);

		if(write(fd , &currentAcc , sizeof(currentAcc)) == -1)
		{
			perror("fail to save");
			return 0;
		}

	//unlock
		fcntl(fd , F_UNLCK , &lock);
		close(fd);
		printf("save the account of id : %d successfully .\n",acc.num);
		return 1;
	}
}


int withdrawAccount(struct Account acc)
{
	char buf[100] = {};	
	sprintf(buf, "./user/%d.dat", acc.num);
	struct Account currentAcc;	
	//1. check if file exists.
	if(access( buf , F_OK) == -1)
	{
		printf("The account of id :%d doesn't exists !\n",acc.num);
		return 0;
	}
	else
	{
		int fd = open(buf , O_RDWR , 0600);
		if(fd == -1)
			perror("fail to open.") , exit(-1);
	//update data of the account.
	//lock the file before being writen.
		struct flock lock;
		lock.l_type = F_WRLCK;
		lock.l_whence = SEEK_SET;
		lock.l_start = 0;
		lock.l_pid = -1;
		int res = fcntl(fd , F_SETLK , &lock);

		read(fd , &currentAcc , sizeof(currentAcc));
		
		if(strcmp(acc.pwd , currentAcc.pwd))
		{
			printf("wrong password!\n");
			return 0;
		}

		if(acc.balance > currentAcc.balance)
		{
			printf("Balance is not enough\n");
			return 0;
		}
		currentAcc.balance -= acc.balance;

	//rewrite , very important ..
		lseek(fd , 0 , SEEK_SET);

		if(write(fd , &currentAcc , sizeof(currentAcc)) == -1)
		{
			perror("fail to withdraw");
			return 0;
		}

	//unlock
		fcntl(fd , F_UNLCK , &lock);
		close(fd);
		printf("save the account of id : %d successfully .\n",acc.num);
		return 1;
	}
}


int queryAccount(struct Account * acc)
{
	char buf[100] = {};	
	sprintf(buf, "./user/%d.dat", acc->num);
	//1. check if file exists.
	struct Account newacc ;
	if(access( buf , F_OK) == -1)
	{
		printf("The account of id :%d doesn't exists !\n",acc->num);
		return 0;
	}
	else
	{
		int fd = open(buf , O_RDWR , 0600);
		if(fd == -1)
			perror("fail to open.") , exit(-1);
	//check data of the account.
	//lock the file before being read.
		struct flock lock;
		lock.l_type = F_RDLCK;
		lock.l_whence = SEEK_SET;
		lock.l_start = 0;
		lock.l_pid = -1;
		int res = fcntl(fd , F_SETLK , &lock);

		if(read(fd , &newacc , sizeof(newacc)) == -1)
		{
			perror("fail to read");
			return 0;
		}

		if(!strcmp(acc->pwd , newacc.pwd))
		{
			strcpy(acc->name , newacc.name);
			acc->balance = newacc.balance;
		}
		else
		{
			printf("wrong password!\n");
			return 0;
		}		

	//unlock
		fcntl(fd , F_UNLCK , &lock);
		close(fd);
		return 1;
	}
}

int transferAccount(struct Account acc1 , struct Account acc2)
{
	char buffrom[100] = {};
	char bufto[100] = {};	
	sprintf(buffrom, "./user/%d.dat", acc1.num);
	struct Account currentAcc;	
	//1. check if file exists.
	if(access( buffrom , F_OK) == -1)
	{
		printf("The account of id %d doesn't exists !\n",acc1.num);
		return 0;
	}
	
	sprintf(bufto, "./user/%d.dat", acc2.num);
	if(access( bufto , F_OK) == -1)
	{
		printf("The account of id you transfer to %d doesn't exists !\n",acc2.num);
		return 0;
	}
	
	int fd1 = open(buffrom , O_RDWR , 0600);
	if(fd1 == -1)
		perror("fail to open fd1.") , exit(-1);
	int fd2 = open(bufto , O_RDWR , 0600);
	if(fd2 == -1)
		perror("fail to open fd2.") , exit(-1);
	//update data of the account.
	//lock the file before being writen.
	struct flock lock1 , lock2;
	lock1.l_type = F_WRLCK;
	lock1.l_whence = SEEK_SET;
	lock1.l_start = 0;
	lock1.l_pid = -1;

	lock2.l_type = F_WRLCK;
	lock2.l_whence = SEEK_SET;
	lock2.l_start = 0;
	lock2.l_pid = -1;
	
	int res = fcntl(fd1 , F_SETLK , &lock1);
	if(res == -1)
		perror("fail to access fd1.") , exit(-1);
	res = fcntl(fd2 , F_SETLK , &lock2);
	if(res == -1)
		perror("fail to access fd2.") , exit(-1);
	
	//check password of acc1
	read(fd1 , &currentAcc , sizeof(currentAcc));
	if(strcmp(acc1.pwd , currentAcc.pwd))
	{
		printf("wrong password!\n");
		return 0;
	}
	//check if balance of acc1 is enough
	if(acc1.balance > currentAcc.balance)
	{
		printf("balance is not enough\n");
		return 0;
	}
	//minus balance of acc1
	currentAcc.balance -= acc1.balance;	
	//rewrite , very important ..
	lseek(fd1 , 0 , SEEK_SET);
	if(write(fd1 , &currentAcc , sizeof(currentAcc)) == -1)
	{
		perror("fail to transfer");
		return 0;
	}
	
	memset(&currentAcc , 0 , sizeof(currentAcc));

	//get balance of acc2
	read(fd2 , &currentAcc , sizeof(currentAcc));
	//add balance
	currentAcc.balance += acc1.balance;
	lseek(fd2 , 0 , SEEK_SET);
	if(write(fd2 , &currentAcc , sizeof(currentAcc)) == -1)
	{
		perror("fail to transfer");
		return 0;
	}

	//unlock
	fcntl(fd2 , F_UNLCK , &lock2);	
	fcntl(fd1 , F_UNLCK , &lock1);
	close(fd2);
	close(fd1);
	return 1;
}

//server
void main()
{
	int msgid1 ;
	int msgid2 ;
	if((msgid1 = msgget(key1,0)) == -1)
	{
		perror("queue 1 fail!") , exit(-1);
	}
	if((msgid2 = msgget(key2,0)) == -1)
	{
		perror("queue 2 fail!") , exit(-1);
	}

	while(1)
	{
		struct Msg m1;
		if(msgrcv(msgid1 , &m1  , sizeof(m1) , 0 , 0) == -1)
			break;
		
		//OPEN
		if(m1.mtype == M_OPEN)
		{
			int id = Findfile("./user");
			struct Account acc;
			acc = m1.acc[0];
			acc.num = id;
			
			if(establishAccount(acc))
			{
				m1.mtype = M_SUCCESS;
			}
			else
			{
				m1.mtype = M_FAIL;
			}
			//m1.acc = queryAccount()
			msgsnd(msgid2 , &m1  , sizeof(m1) , 0 );
		}
		//DESTORY
		else if(m1.mtype == M_DESTORY)
		{
			if(closeAccount(m1.acc[0]))
			{
				m1.mtype = M_SUCCESS;
			}
			else
			{
				m1.mtype = M_FAIL;
			}
			msgsnd( msgid2 , &m1 , sizeof(m1) , 0 );
		}
		//SAVE
		else if(m1.mtype == M_SAVE)
		{
			if(saveAccount(m1.acc[0]))
			{
				m1.mtype = M_SUCCESS;
			}
			else
			{
				m1.mtype = M_FAIL;
			}
			msgsnd( msgid2 , &m1 , sizeof(m1) , 0 );
		}
		
		//WITHDRAW
		else if(m1.mtype == M_WITHDRAW)
		{
			if(withdrawAccount(m1.acc[0]))
			{
				m1.mtype = M_SUCCESS;
			}
			else
			{
				m1.mtype = M_FAIL;
			}
			msgsnd( msgid2 , &m1 , sizeof(m1) , 0 );
		}

		//QUERY
		else if(m1.mtype == M_QUERY)
		{
			if(queryAccount(&m1.acc[0]))
			{
				m1.mtype = M_SUCCESS;
			}
			else
			{
				m1.mtype = M_FAIL;
			}
			msgsnd( msgid2 , &m1 , sizeof(m1) , 0 );
		}

		//TRANSFER
		else if(m1.mtype == M_TRANSFER)
		{
			if(transferAccount(m1.acc[0] , m1.acc[1]))
			{
				m1.mtype = M_SUCCESS;
			}
			else
			{
				m1.mtype = M_FAIL;
			}
			msgsnd( msgid2 , &m1 , sizeof(m1) , 0 );
		}
		
		else
		{
			
		}
	}
}
